//! OpenTelemetry格式导出器
//! 
//! 将追踪数据导出为OpenTelemetry标准格式

use crate::*;
use async_trait::async_trait;
use serde_json::json;
use std::time::UNIX_EPOCH;

/// OpenTelemetry导出器
pub struct OpenTelemetryExporter {
    service_name: String,
    service_version: String,
    resource_attributes: std::collections::HashMap<String, String>,
}

impl OpenTelemetryExporter {
    /// 创建新的OpenTelemetry导出器
    pub fn new(service_name: &str, service_version: &str) -> Self {
        let mut resource_attributes = std::collections::HashMap::new();
        resource_attributes.insert("service.name".to_string(), service_name.to_string());
        resource_attributes.insert("service.version".to_string(), service_version.to_string());
        
        Self {
            service_name: service_name.to_string(),
            service_version: service_version.to_string(),
            resource_attributes,
        }
    }

    /// 添加资源属性
    pub fn add_resource_attribute(&mut self, key: &str, value: &str) {
        self.resource_attributes.insert(key.to_string(), value.to_string());
    }
}

#[async_trait]
impl TraceExporter for OpenTelemetryExporter {
    async fn export(&self, spans: &[TraceSpan]) -> TracingResult<String> {
        if spans.is_empty() {
            return Ok("{}".to_string());
        }

        let otel_spans: Vec<_> = spans.iter().map(|span| {
            // 转换为nanoseconds
            let start_time_nano = span.start_time.duration_since(UNIX_EPOCH)
                .unwrap_or_default().as_nanos();
            let end_time_nano = span.finish_time
                .and_then(|ft| ft.duration_since(UNIX_EPOCH).ok())
                .map(|d| d.as_nanos())
                .unwrap_or(start_time_nano);

            // 构建attributes
            let mut attributes = Vec::new();
            for (key, value) in &span.tags {
                attributes.push(json!({
                    "key": key,
                    "value": {
                        "stringValue": value
                    }
                }));
            }

            // 构建events (logs)
            let events: Vec<_> = span.logs.iter().map(|log| {
                let mut event_attributes = Vec::new();
                for (key, value) in &log.fields {
                    event_attributes.push(json!({
                        "key": key,
                        "value": {
                            "stringValue": value
                        }
                    }));
                }

                json!({
                    "timeUnixNano": log.timestamp.duration_since(UNIX_EPOCH)
                        .unwrap_or_default().as_nanos().to_string(),
                    "name": log.message,
                    "attributes": event_attributes
                })
            }).collect();

            json!({
                "traceId": hex::encode(span.trace_id.as_bytes()),
                "spanId": hex::encode(span.span_id.as_bytes()),
                "parentSpanId": span.parent_span_id.as_ref()
                    .map(|id| hex::encode(id.as_bytes()))
                    .unwrap_or_default(),
                "name": span.operation_name,
                "kind": "SPAN_KIND_INTERNAL",
                "startTimeUnixNano": start_time_nano.to_string(),
                "endTimeUnixNano": end_time_nano.to_string(),
                "attributes": attributes,
                "events": events,
                "status": {
                    "code": if span.tags.get("error").map(|v| v == "true").unwrap_or(false) {
                        "STATUS_CODE_ERROR"
                    } else {
                        "STATUS_CODE_OK"
                    }
                }
            })
        }).collect();

        // 构建资源信息
        let mut resource_attributes = Vec::new();
        for (key, value) in &self.resource_attributes {
            resource_attributes.push(json!({
                "key": key,
                "value": {
                    "stringValue": value
                }
            }));
        }

        let result = json!({
            "resourceSpans": [{
                "resource": {
                    "attributes": resource_attributes
                },
                "scopeSpans": [{
                    "scope": {
                        "name": "rustcloud-tracing",
                        "version": "1.0.0"
                    },
                    "spans": otel_spans
                }]
            }]
        });

        serde_json::to_string_pretty(&result)
            .map_err(|e| Box::new(TracingError::ExportError(e.to_string())) as Box<dyn std::error::Error + Send + Sync>)
    }

    fn name(&self) -> &str {
        "opentelemetry"
    }

    fn supported_format(&self) -> TraceExportFormat {
        TraceExportFormat::OpenTelemetry
    }
}

#[cfg(feature = "otlp")]
pub mod otlp_client {
    use super::*;
    
    /// OTLP客户端，用于向OpenTelemetry Collector发送数据
    pub struct OtlpClient {
        endpoint: String,
        headers: std::collections::HashMap<String, String>,
        client: reqwest::Client,
    }

    impl OtlpClient {
        /// 创建新的OTLP客户端
        pub fn new(endpoint: &str) -> Self {
            Self {
                endpoint: endpoint.to_string(),
                headers: std::collections::HashMap::new(),
                client: reqwest::Client::new(),
            }
        }

        /// 添加认证头
        pub fn with_header(mut self, key: &str, value: &str) -> Self {
            self.headers.insert(key.to_string(), value.to_string());
            self
        }

        /// 发送追踪数据到OTLP端点
        pub async fn send_traces(&self, spans: &[TraceSpan], service_name: &str, service_version: &str) -> TracingResult<()> {
            let exporter = OpenTelemetryExporter::new(service_name, service_version);
            let data = exporter.export(spans).await?;

            let mut request = self.client
                .post(&format!("{}/v1/traces", self.endpoint))
                .header("Content-Type", "application/json");

            // 添加自定义头
            for (key, value) in &self.headers {
                request = request.header(key, value);
            }

            let response = request
                .body(data)
                .send()
                .await
                .map_err(|e| Box::new(TracingError::NetworkError(e.to_string())) as Box<dyn std::error::Error + Send + Sync>)?;

            if !response.status().is_success() {
                return Err(Box::new(TracingError::NetworkError(
                    format!("OTLP endpoint responded with status: {}", response.status())
                )));
            }

            Ok(())
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::collections::HashMap;

    #[tokio::test]
    async fn test_opentelemetry_exporter() {
        let mut exporter = OpenTelemetryExporter::new("test-service", "1.0.0");
        exporter.add_resource_attribute("environment", "test");
        
        let span = TraceSpan {
            trace_id: "test-trace-123".to_string(),
            span_id: "test-span-456".to_string(),
            parent_span_id: Some("parent-span-789".to_string()),
            operation_name: "test-operation".to_string(),
            start_time: std::time::SystemTime::now(),
            finish_time: Some(std::time::SystemTime::now()),
            tags: {
                let mut tags = HashMap::new();
                tags.insert("http.method".to_string(), "GET".to_string());
                tags.insert("http.status_code".to_string(), "200".to_string());
                tags
            },
            logs: vec![
                SpanLog {
                    timestamp: std::time::SystemTime::now(),
                    message: "Request started".to_string(),
                    fields: {
                        let mut fields = HashMap::new();
                        fields.insert("level".to_string(), "info".to_string());
                        fields
                    },
                }
            ],
        };

        let result = exporter.export(&[span]).await;
        assert!(result.is_ok());
        
        let exported = result.unwrap();
        assert!(exported.contains("resourceSpans"));
        assert!(exported.contains("test-service"));
        assert!(exported.contains("test-operation"));
        assert!(exported.contains("http.method"));
    }

    #[tokio::test]
    async fn test_opentelemetry_error_span() {
        let exporter = OpenTelemetryExporter::new("error-service", "1.0.0");
        
        let span = TraceSpan {
            trace_id: "error-trace-123".to_string(),
            span_id: "error-span-456".to_string(),
            parent_span_id: None,
            operation_name: "error-operation".to_string(),
            start_time: std::time::SystemTime::now(),
            finish_time: Some(std::time::SystemTime::now()),
            tags: {
                let mut tags = HashMap::new();
                tags.insert("error".to_string(), "true".to_string());
                tags.insert("error.message".to_string(), "Test error".to_string());
                tags
            },
            logs: vec![],
        };

        let result = exporter.export(&[span]).await;
        assert!(result.is_ok());
        
        let exported = result.unwrap();
        assert!(exported.contains("STATUS_CODE_ERROR"));
        assert!(exported.contains("Test error"));
    }
}